﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

using LowLevelGraphics.ColorSpaces;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Extract Channel of ColorSpace
    /// </summary>
    public class ChannelExtract : BaseImageFilter
    {
        protected AbstractColorSpace m_AbstractColorSpace = new YUV(Color.Black);
        protected int[] m_aChannel = new int[] { 0, 2 };

        /// <summary>
        /// Initializes a new instance of the <see cref="ChannelExtract"/> class.
        /// </summary>
        public ChannelExtract()
        {
        }

        /// <summary>
        /// used by cloning
        /// </summary>
        /// <param name="_channelExtract"></param>
        internal ChannelExtract(ChannelExtract _channelExtract)
        {
            m_AbstractColorSpace = _channelExtract.m_AbstractColorSpace;
            m_aChannel = _channelExtract.m_aChannel;
        }

        /// <summary>
        /// Gets or sets the color space.
        /// </summary>
        /// <value>The color space.</value>
        public AbstractColorSpace ColorSpace
        {
            get { return m_AbstractColorSpace; }
            set { m_AbstractColorSpace = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int y = 0;
            int x = 0;
            int nWidth = _bitmap.Width;
            int nHeight = _bitmap.Height;
            int i = 0;
            //Get Length of array to seek.
            int nLength = m_AbstractColorSpace.GetValues().Length;
            object[] aObject = null;

            //Sort given channels
            List<int> aChannel = SortChannels();

            //Missing channels have to be set to zero.
            List<int> aSetToZero = GetMissingChannelArray(nLength, aChannel);

            int nSetToZeroCount = aSetToZero.Count;
            //This is already optimized.
            //For more optimization one would have to change AbstractColorSpace I guess

            for (y = 0; y < nHeight; y++)
            {
                for (x = 0; x < nWidth; x++)
                {
                    m_AbstractColorSpace.Color = _bitmap.GetPixel(x, y);
                    aObject = m_AbstractColorSpace.GetValues();

                    for (i = 0; i < nSetToZeroCount; i++)
                    {
                        aObject[aSetToZero[i]] = 0.0f;
                    }

                    m_AbstractColorSpace.SetValues(aObject);
                    _bitmap.SetPixel(x, y, m_AbstractColorSpace.ToColor());
                }
            }
            return _bitmap;
        }

        /// <summary>
        /// Sorts the channels.
        /// </summary>
        /// <returns></returns>
        private List<int> SortChannels()
        {
            List<int> aChannel = new List<int>();
            aChannel.AddRange(m_aChannel);
            aChannel.Sort();
            return aChannel;
        }

        /// <summary>
        /// Gets the missing channel array.
        /// </summary>
        /// <param name="nLength">Length of the n.</param>
        /// <param name="aChannel">A channel.</param>
        /// <returns></returns>
        private static List<int> GetMissingChannelArray(int nLength, List<int> aChannel)
        {
            List<int> aSetToZero = new List<int>();
            for (int i = 0; i < nLength; i++)
            {
                if (!aChannel.Contains(i))
                {
                    aSetToZero.Add(i);
                }
            }
            return aSetToZero;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ChannelExtract(this);
        }
    }
}
